﻿using System;
using System.Collections.Generic;
using System.Net;
using System.Text.RegularExpressions;
using IEx.Common;
using IEx.Common.Sources;
using IEx.Model.Partials;
using IEx.Utilities;

namespace IEx.ParserBuilder.Tinhte
{
    public class TinhteParser : IHtmlParser
    {
        /// <summary>
        /// 
        /// </summary>
        /// <param name="item"></param>
        /// <param name="html"></param>
        /// <param name="attachResultItem"></param>
        /// <param name="attachNewItemSource"></param>
        public void Parse(SourceItem item, string html, Action<Model.Partials.Announcement> attachResultItem, Action<SourceItem> attachNewItemSource)
        {
            try
            {
                string pageDesId = "id=\"pageDescription\"";
                string contentSummaryClass = "class=\"contentSummary\"";
                string remainHtml = Utility.GetHtmlBody(html, pageDesId, contentSummaryClass);
                List<Announcement> detectedAnnouncements = new List<Announcement>();
                Uri newUri = Utility.CreateNewUriForNextPage(item.Url);

                if (!string.IsNullOrEmpty(remainHtml))
                {
                    string dateTime = string.Empty;
                    string user = string.Empty;
                    string userLinkDetail = string.Empty;
                    string threadLinkDetail = string.Empty;
                    string threadTitle = string.Empty;
                    DateTime postDate = DateTime.MinValue;

                    Regex regThreads = new Regex("<li.*? class=\"discussionListItem visible.*?\" data-author=\".*?\">.*?</li>");
                    MatchCollection mThreads = regThreads.Matches(remainHtml);
                    for (int i = 0; i < mThreads.Count; i++)
                    {
                        string threadHtml = mThreads[i].Value;
                        Regex regTitle = new Regex("<h3 class=\"title\">.*?</h3>");
                        Match mTitle = regTitle.Match(threadHtml);
                        if (mTitle.Success)
                        {
                            string titleHtml = mTitle.Value;
                            threadLinkDetail = new Uri(newUri, RegexUtility.ExtractHyperlink(titleHtml)).ToString();
                            threadTitle = RegexUtility.ExtractTextFromHtmlTag(titleHtml);
                        }
                        Regex regUserBlock = new Regex("<div class=\"posterDate muted\">.*?</div>");
                        Match mUserBlock = regUserBlock.Match(threadHtml);
                        if (mUserBlock.Success)
                        {
                            string userBlockHtml = mUserBlock.Value;
                            Regex regUser = new Regex("<a href=\".*?\" class=\"username\" dir=\"auto\".*?>.*?</a>");
                            Match mUser = regUser.Match(userBlockHtml);
                            if (mUser.Success)
                            {
                                string userHtml = mUser.Value;
                                user = RegexUtility.ExtractTextFromHtmlTag(userHtml);
                                userLinkDetail = RegexUtility.ExtractHyperlink(userHtml);
                            }
                        }
                        Regex regDateTime = new Regex("<span class=\"DateTime\".*?>.*?</span>");
                        Match mDateTime = regDateTime.Match(threadHtml);
                        if (mDateTime.Success)
                        {
                            string dateTimeHtml = mDateTime.Value;
                            dateTime = RegexUtility.ExtractTextFromHtmlTag(dateTimeHtml);
                            if (!string.IsNullOrEmpty(dateTime))
                            {
                                postDate = Utility.ConvertToDateTimeObject(dateTime, item.DateTimeFormat);
                            }
                        }
                        List<Comment> comments = new List<Comment>();
                        if (!string.IsNullOrEmpty(threadLinkDetail))
                            ExtractCommentsTinhte(threadLinkDetail, item, ref comments);
                        if (!string.IsNullOrEmpty(threadLinkDetail) && !string.IsNullOrEmpty(threadTitle)
                            && comments.Count > 0 && !string.IsNullOrEmpty(user))
                        {
                            Announcement ann = new Announcement();
                            ann.DateTime = postDate;
                            ann.DateNumberic = long.Parse(
                                            postDate.ToString("yyyyMMddHHmmss"));
                            ann.SocialNetwork.ScreenName = user;
                            ann.InsertedDate = DateTime.Now;
                            ann.LangId = item.LangId;
                            ann.Title = threadTitle;
                            ann.SourceCode = item.SourceCode;
                            ann.SourceId = item.SourceId;
                            ann.InsertedDate = DateTime.Now;
                            ann.Link = threadLinkDetail;
                            ann.SocialNetwork.Comments = comments;
                            detectedAnnouncements.Add(ann);
                        }
                    }
                    //Regex get url for next page
                    Regex regNextPage = new Regex("class=\"currentPage +\".*?>.*?</a>.*?<a href=\".*?\">.*?</a>");
                    Match mNextPage = regNextPage.Match(remainHtml);
                    if (mNextPage.Success)
                    {
                        string nextPageHtml = mNextPage.Value;
                        string link = RegexUtility.ExtractHyperlink(nextPageHtml);
                        if (!string.IsNullOrEmpty(link))
                            item.Url = new Uri(newUri, link).ToString();
                    }
                }

                // check if has next page 
                // next page counts as a new request, so it needed insert into request queue

                if (CheckNextPageTinhte(html))
                {
                    SourceItem nItem = new SourceItem(item.Url, item.LangId, item.SourceCode, item.SearchCode, item.SourceId, item.DateTimeFormat);
                    if (!string.IsNullOrEmpty(item.RequestBuilderType))
                        nItem.RequestBuilderType = item.RequestBuilderType;

                    if (!string.IsNullOrEmpty(item.HtmlParserType))
                        nItem.HtmlParserType = item.HtmlParserType;

                    nItem.PrevResponseHtml = html;
                    attachNewItemSource(nItem);
                }

                // insert into download queue when no error in parsing
                foreach (Announcement ann in detectedAnnouncements)
                {
                    attachResultItem(ann);
                }

                // if this request is fail request in previous scanning, 
                // remove it when success in current scanning
                if (item.FailId > 0)
                    Utility.DeleteFailRequests(item.FailId);
            }
            catch (Exception ex)
            {
                // if has an error, save fail request to next time scanning
                if (item.FailId == 0)
                {
                    item.FailId = DateTime.Now.Ticks;
                    Utility.SaveFailRequests(item);
                }

                // then write log
                string message = string.Format(@"
            Company code:   {0}
            HTML Parsing:   Fail
            Request URL:    {1}
            Search key:     {2}", item.SourceCode, item.Url, item.SearchCode);
                Logger.WriteError(message, ex);
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="url"></param>
        /// <param name="item"></param>
        /// <param name="comments"></param>
        private static void ExtractCommentsTinhte(string url, SourceItem item, ref List<Comment> comments)
        {
            Logger.Debug("***SCANNING POST COMMENTS URL: " + url);
            HttpStatusCode status = HttpStatusCode.Accepted;
            string html = Utility.LoadHtml(url, ref status);
            Uri newUri = item.SourceId == (int)SourceIds.Lamchame ? Utility.CreateNewUriForNextPage(url, 1) : Utility.CreateNewUriForNextPage(url);
            string nextPageUrl = url;
            try
            {
                if (!string.IsNullOrEmpty(html))
                {
                    string startHtml = "id=\"pageDescription\"";
                    string endHtml = "class=\"sidebar\"";
                    string remainHtml = Utility.GetHtmlBody(html, startHtml, endHtml);

                    if (!string.IsNullOrEmpty(remainHtml))
                    {
                        Regex regCommentContent = new Regex("<li id=\".*?\" class=\"message .*?\" data-author=\".*?\">.*?</li>");
                        MatchCollection mCommentContent = regCommentContent.Matches(remainHtml);
                        for (int i = 0; i < mCommentContent.Count; i++)
                        {
                            string user = string.Empty;
                            string userLinkDetail = string.Empty;
                            string content = string.Empty;
                            string dateTime = string.Empty;
                            DateTime postDate = DateTime.Now;
                            string contentCommentHtml = mCommentContent[i].Value;

                            Regex regUserBlock = new Regex("<h3 class=\"userText\">.*?</h3>");
                            Match mUserBlock = regUserBlock.Match(contentCommentHtml);
                            if (mUserBlock.Success)
                            {
                                string userBlockHtml = mUserBlock.Value;
                                Regex regUser = new Regex("<a href=\".*?\" class=\"username\" dir=\"auto\".*?>.*?</a>");
                                Match mUser = regUser.Match(userBlockHtml);
                                if (mUser.Success)
                                {
                                    string userHtml = mUser.Value;
                                    user = RegexUtility.ExtractTextFromHtmlTag(userHtml);
                                    userLinkDetail = RegexUtility.ExtractHyperlink(userHtml);
                                }
                            }
                            Regex regContentBlock = new Regex("<blockquote class=\".*?\">.*?</blockquote>");
                            Match mContentBlock = regContentBlock.Match(contentCommentHtml);
                            if (mContentBlock.Success)
                            {
                                string contentHtml = mContentBlock.Value;
                                content = RegexUtility.ExtractTextFromHtmlTag(contentHtml);
                            }
                            Regex regDateTime = new Regex("<span class=\"DateTime\".*?>.*?</span>");
                            Match mDateTime = regDateTime.Match(contentCommentHtml);
                            if (mDateTime.Success)
                            {
                                string dateTimeHtml = mDateTime.Value;
                                dateTime = RegexUtility.ExtractTextFromHtmlTag(dateTimeHtml);
                                if (!string.IsNullOrEmpty(dateTime))
                                {
                                    postDate = Utility.ConvertToDateTimeObject(dateTime, item.DateTimeFormat);
                                }
                            }
                            if (!string.IsNullOrEmpty(content) && !string.IsNullOrEmpty(user))
                            {
                                comments.Add(new Comment()
                                {
                                    CommentContent = content,
                                    DateTime = postDate,
                                    UserName = user,
                                    DateNumberic = long.Parse(
                                        postDate.ToString("yyyyMMddHHmmss")),
                                });
                            }
                        }
                        //Regex get url for next page
                        Regex regNextPage = new Regex("class=\"currentPage +\".*?>.*?</a>.*?<a href=\".*?\">.*?</a>");
                        Match mNextPage = regNextPage.Match(remainHtml);
                        if (mNextPage.Success)
                        {
                            string nextPageHtml = mNextPage.Value;
                            string link = RegexUtility.ExtractHyperlink(nextPageHtml);
                            if (!string.IsNullOrEmpty(link))
                                nextPageUrl = new Uri(newUri, link).ToString();
                        }
                    }
                }
                // if this request is fail request in previous scanning, 
                // remove it when success in current scanning
                if (item.FailId > 0)
                    Utility.DeleteFailRequests(item.FailId);

                Logger.WriteLogInfo("***DONE SCANNING POST COMMENT URL: " + url);
            }
            catch (Exception ex)
            {
                // then write log
                string message = string.Format(@"
            Extract post-comment details
            Source code:   {0}
            HTML Parsing:   Fail
            Request URL:    {1}
            Page number:     {2}", item.SourceCode, url, nextPageUrl);
                Logger.WriteError(message, ex);
            }
            // check if has next page 
            // next page counts as a new request, so it needed insert into request queue
            if (CheckNextPageTinhte(html) && string.Compare(url, nextPageUrl, false) != 0)
            {
                ExtractCommentsTinhte(nextPageUrl, item, ref comments);
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="response"></param>
        /// <returns></returns>
        private static bool CheckNextPageTinhte(string response)
        {
            return response.Contains("class=\"text\">Sau &gt;</a>");
        }
    }
}
